package com.ylz.sjzx.sio.sdk.client.utils;

import cn.hutool.core.util.StrUtil;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.Transport;
import com.corundumstudio.socketio.handler.TransportState;
import com.corundumstudio.socketio.transport.NamespaceClient;
import com.ylz.sjzx.sio.common.core.constant.Const;
import com.ylz.sjzx.sio.common.core.constant.IMConstant;
import com.ylz.sjzx.sio.common.core.context.ApplicationUtils;
import com.ylz.sjzx.sio.common.core.enums.Role;
import com.ylz.sjzx.sio.common.core.enums.SocketModel;
import com.ylz.sjzx.sio.common.core.exception.LoginException;
import com.ylz.sjzx.sio.common.core.utils.DateUtil;
import com.ylz.sjzx.sio.common.core.utils.GsonUtil;
import com.ylz.sjzx.sio.common.core.utils.HostUtil;
import com.ylz.sjzx.sio.common.core.utils.ReactUtil;
import com.ylz.sjzx.sio.sdk.client.service.IMSessionService;
import com.ylz.sjzx.sio.sdk.client.service.UserSessionService;
import com.ylz.sjzx.sio.sdk.client.session.IMSession;
import com.ylz.sjzx.sio.sdk.model.BaseSocketModel;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author wiqi
 * @version 1.0
 * @date 2020/12/8
 */
@Slf4j
public class SessionUtil {

    private static IMSessionService iMSessionService;

    private static UserSessionService userSessionService;


    /**
     * 转session
     *
     * @param channel
     * @param surl
     * @return
     */
    public static IMSession parse2Session(Channel channel, String surl)
            throws UnsupportedEncodingException {
        String decodeUrl = URLDecoder.decode(surl, Const.UTF8);
        IMSession session = null;
        Map<String, String> uriMap = parseUri2Map(decodeUrl);
        session = parseMap2Session(uriMap);
        session.setChannel(channel);
        session.setSid(channel.id().asShortText());
        session.setSocketModel(SocketModel.channel);

        return session;
    }

    /**
     * 转session
     *
     * @param
     * @param
     * @return
     */
    public static IMSession parse2Session(SocketIOClient socketIOClient, Map<String, String> map) {
        IMSession session = parseMap2Session(map);
        //模式为socketIo
        if (socketIOClient != null) {
            session.setSocketIOClient(socketIOClient);
            session.setSid(socketIOClient.get(IMConstant.IMSESSIONID));
        }
        session.setSocketModel(SocketModel.socketIo);
        return session;
    }

    /**
     * 转session
     *
     * @param
     * @param
     * @return
     */
    public static IMSession parseMap2Session(Map<String, String> map) {
        IMSession session = null;
        try {
            String user = map.get("user");
            session = GsonUtil.json2Bean(user, IMSession.class);
            if (StrUtil.isNotBlank(map.get("socketType"))) {
                session.setSocketType(map.get("socketType"));

            }
            if (StrUtil.isBlank(session.getUserId())) {
                Object userId = GsonUtil.json2Bean(user, Map.class).get("UserId");
                session.setUserId(user == null ? null : userId.toString());
            }
            session.setMpFlag(Objects.equals(Const.MP, session.getSocketType()));
            session.setStudent(Objects.equals(Role.st, session.getRole()));
            session.setLoginTime(DateUtil.format(new Date(), DateUtil.DATE_FORMAT_YYYY_MM_DD_HH_MM_SS));
            session.setAvatar(session.getAvatar() == null ? "" : URLDecoder.decode(session.getAvatar(), "utf-8"));
        } catch (Exception e) {
            throw new LoginException("用户解析异常", e);
        }
        //用户所在宿主机 增加端口号
        session.setHostAddress(HostUtil.getCompleteAddress());

        session.setDeviceType(StrUtil.isBlank(session.getDeviceType()) ? "pc" : session.getDeviceType());
        return session;
    }

    public static Map<String, String> parseUri2Map(String surl) {
        Map<String, String> map = new HashMap<>(16);
        String[] split = surl.split("&");
        for (String paramKV : split) {
            String[] kv = paramKV.split("=");
            map.put(kv[0], kv[1]);
        }
        return map;
    }

    public static IMSessionService getIMSessionService() {
        if (iMSessionService == null) {
            iMSessionService = ApplicationUtils.getBean(IMSessionService.class);
        }
        return iMSessionService;
    }

    /**
     * 获取用户Session
     *
     * @return
     */
    public static UserSessionService getUserSessionService() {
        if (userSessionService == null) {
            //默认获取的是二级缓存
            userSessionService = ApplicationUtils.getBean("userL2SessionServiceImpl", UserSessionService.class);
        }
        return userSessionService;
    }


    public static Channel getChannle(NamespaceClient socketIOClient) {
        //反射获取BaseClient 的channels
        Map<Transport, TransportState> channels =
                (Map<Transport, TransportState>) ReactUtil
                        .getFieldValueByFieldName("channels", socketIOClient.getBaseClient());
        TransportState state = channels.get(socketIOClient.getBaseClient().getCurrentTransport());

        Channel channel = state.getChannel();
        if (channel == null) {
            throw new LoginException("登录异常");
        }
        return channel;
    }

    /**
     * 正常发送管道消息
     *
     * @param session
     * @param message
     */
    public static void sendMesage(IMSession session, BaseSocketModel message) {
        if (session.rightSocket() == null) {
            log.error("用户的连接拿不到 可能已经断开了{}", session);
            return;
        }
        if (Objects.equals(SocketModel.channel, session.getSocketModel())) {
            session.getChannel().writeAndFlush(new TextWebSocketFrame(message.toCmdMessage()));
        } else {
            if (message.isNeedtoParam()) {
                SocketIOClient socketIOClient = session.getSocketIOClient();
                socketIOClient.sendEvent(message.getAction().getValue(), message.getParam());
            } else {
                session.getSocketIOClient().sendEvent(message.getAction().getValue(), message.toMessage());
            }
        }
    }
}
